#include <QMessageBox>
#include <QFileInfo>
#include <QStringList>
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);

    int w = this->width();
    int h = this->height();
    this->ui->article_tree_widget->setGeometry(0, 0, w * 0.3, h);
    this->ui->title_line_edit->setGeometry(w * 0.3, 0, w * 0.7, 30);
    this->ui->content_text_edit->setGeometry(w * 0.3, 31, w * 0.7, h - 31);

    this->ui->title_line_edit->setEnabled(false);
    this->ui->content_text_edit->setEnabled(false);

    bool is_db_exist = true;
    QFileInfo info("textbook.db");
    if (!info.isFile()) {
        is_db_exist = false;
    }

    this->db = QSqlDatabase::addDatabase("QSQLITE");
    this->db.setDatabaseName("textbook.db");
    if (!this->db.open()) {
        QMessageBox::critical(this, "System Error", "Database cannot open.");
        exit(0);
    }

    if (!is_db_exist) {
        QStringList init_sql;
        init_sql << "CREATE TABLE `article`("
                    "`id` INTEGER PRIMARY KEY AUTOINCREMENT,"
                    "`article_name` TEXT"
                    ")"
                 << "CREATE TABLE `node`("
                    "`id` INTEGER PRIMARY KEY AUTOINCREMENT,"
                    "`article_id` INT(11),"
                    "`parent_node_id` INT(11),"
                    "`node_name` TEXT,"
                    "`content` TEXT"
                    ")";
        for (QString sql : init_sql) {
            this->q_exec(sql);
        }
    }

    this->ui->article_tree_widget->setHeaderHidden(true);
    this->flush_tree();

    this->info.id = -1;
    this->info.is_node = false;

    this->new_article = new NewArticle(this);
    this->new_node = new NewNode(this);
    this->connect(this->new_article, SIGNAL(send_article(QString)), this, SLOT(receive_article(QString)));
    this->connect(this->new_node, SIGNAL(send_node(int,int,QString,QString)), this, SLOT(receive_node(int,int,QString,QString)));
}

void MainWindow::next_tree(QTreeWidgetItem *item, int article_id, int parent_node_id) {
    QSqlQuery *q = this->q_exec("SELECT `id`, `node_name` FROM `node` WHERE `article_id` = " +
                                QVariant(article_id).toString() + " AND `parent_node_id` = " +
                                QVariant(parent_node_id).toString() + " ORDER BY `id` ASC", false);
    while (q->next()) {
        QTreeWidgetItem *i = new QTreeWidgetItem;
        i->setText(0, q->value("node_name").toString());
        item->addChild(i);
        this->next_tree(i, article_id, q->value("id").toInt());
    }
    delete q;
}

void MainWindow::flush_tree() {
    this->ui->article_tree_widget->clear();
    QSqlQuery *q = this->q_exec("SELECT `id`, `article_name` FROM `article` ORDER BY `id` ASC", false);
    while (q->next()) {
        QTreeWidgetItem *i = new QTreeWidgetItem(this->ui->article_tree_widget);
        i->setText(0, q->value("article_name").toString());
        this->next_tree(i, q->value("id").toInt(), -1);
    }
    delete q;
    this->ui->article_tree_widget->expandAll();
}

QSqlQuery *MainWindow::q_exec(QString sql, bool is_exit) {
    QSqlQuery *q = new QSqlQuery(this->db);
    if (!q->exec(sql)) {
        this->err = q->lastError();
        QMessageBox::critical(this, "System Error", "SQL exec failed. sql: " + sql +
                              "\nnote: " + this->err.databaseText());
        delete q;
        if (is_exit) {
            this->db.close();
            QSqlDatabase::removeDatabase(this->db.connectionName());
            delete this->new_article;
            delete ui;
            exit(0);
        }
        return nullptr;
    }
    return q;
}

MainWindow::~MainWindow()
{
    this->db.close();
    QSqlDatabase::removeDatabase(this->db.connectionName());
    delete this->new_article;
    delete ui;
}

void MainWindow::resizeEvent(QResizeEvent *event) {
    QSize size = event->size();
    int w = size.width();
    int h = size.height();
    this->ui->article_tree_widget->setGeometry(0, 0, w * 0.3, h);
    this->ui->title_line_edit->setGeometry(w * 0.3, 0, w * 0.7, 30);
    this->ui->content_text_edit->setGeometry(w * 0.3, 31, w * 0.7, h - 30);
}

void MainWindow::keyPressEvent(QKeyEvent *event) {
    if (event->key() == 16777268) {
        this->flush_tree();
    }
}

void MainWindow::on_new_article_triggered() {
    this->new_article->exec();
}

void MainWindow::receive_article(QString title) {
    this->q_exec("INSERT INTO `article`(`article_name`) VALUES ('" + title + "')", false);
    this->flush_tree();
}

void MainWindow::receive_node(int article_id, int parent_node_id, QString title, QString content) {
    this->q_exec("INSERT INTO `node`(`article_id`, `parent_node_id`, `node_name`, `content`) VALUES(" +
                 QVariant(article_id).toString() + ", " + QVariant(parent_node_id).toString() +
                 ", '" + title + "', '" + content + "')", false);
    this->flush_tree();
}

ArticleReturn MainWindow::next_article(ArticleReturn ret, int article_id, int node_id) {
    QList<Article> article;
    QSqlQuery *q = this->q_exec("SELECT `id`, `node_name`, `parent_node_id` FROM `node` WHERE `article_id` = " +
                                QVariant(article_id).toString() + " AND `parent_node_id` = " +
                                QVariant(node_id).toString() + " ORDER BY `id` ASC", false);
    while (q->next()) {
        ++ret.combo_index;
        int id = q->value("id").toInt();
        Article a;
        a.id = id;
        a.combo_index = ret.combo_index;
        a.type = 1;
        a.article_id = article_id;
        a.parent_node_id = q->value("parent_node_id").toInt();
        a.name = q->value("node_name").toString();
        ret = this->next_article(ret, article_id, id);
        a.article_list = ret.article;
        article.append(a);
    }
    delete q;
    ret.article = article;
    return ret;
}

void MainWindow::on_new_node_triggered() {
    this->reset_article_list();
    this->new_node->set_article_list(this->article_list);
    this->new_node->exec();
}

ArticleInfo MainWindow::sub_node_info(int line, QList<Article> list) {
    ArticleInfo info;
    info.is_node = false;
    for (Article a : list) {
        if (a.combo_index == line) {
            if (a.type != 1) {
                return info;
            } else {
                QSqlQuery *q = this->q_exec(
                            "SELECT `node_name`, `content` FROM `node` WHERE `id` = " +
                            QVariant(a.id).toString() + " LIMIT 1", false);
                if (q == nullptr) {
                    return info;
                }
                if (q->next()) {
                    info.is_node = true;
                    info.id = a.id;
                    info.title = q->value("node_name").toString();
                    info.content = q->value("content").toString();
                }
                delete q;
                return info;
            }
        } else {
            info = this->sub_node_info(line, a.article_list);
            if (info.is_node) {
                return info;
            }
        }
    }
    return info;
}

ArticleInfo MainWindow::node_info(int line) {
    ArticleInfo info;
    info.is_node = false;
    for (Article a : this->article_list) {
        if (a.combo_index == line) {
            if (a.type != 1) {
                return info;
            } else {
                QSqlQuery *q = this->q_exec(
                            "SELECT `node_name`, `content` FROM `node` WHERE `id` = " +
                            QVariant(a.id).toString() + " LIMIT 1", false);
                if (q == nullptr) {
                    return info;
                }
                if (q->next()) {
                    info.is_node = true;
                    info.id = a.id;
                    info.title = q->value("node_name").toString();
                    info.content = q->value("content").toString();
                }
                delete q;
                return info;
            }
        } else {
            info = this->sub_node_info(line, a.article_list);
            if (info.is_node) {
                return info;
            }
        }
    }
    return info;
}

void MainWindow::reset_article_list() {
    this->article_list.clear();
    QSqlQuery *q = this->q_exec("SELECT `id`, `article_name` FROM `article` ORDER BY `id` ASC", false);
    if (q == nullptr) {
        return;
    }
    ArticleReturn ret;
    ret.combo_index = -1;
    while (q->next()) {
        ++ret.combo_index;
        Article article;
        article.id = -1;
        article.combo_index = ret.combo_index;
        article.type = 0;
        article.article_id = q->value("id").toInt();
        article.parent_node_id = -1;
        article.name = q->value("article_name").toString();
        ret = this->next_article(ret, article.article_id, -1);
        article.article_list = ret.article;
        this->article_list.append(article);
    }
    delete q;
}

void MainWindow::on_article_tree_widget_itemClicked(QTreeWidgetItem *item, int) {
    this->reset_article_list();
    QTreeWidgetItemIterator item_iter(this->ui->article_tree_widget);
    int line = 0;
    while (*item_iter) {
        if ((*item_iter) == item) {
            break;
        }
        ++line;
        ++item_iter;
    }
    ArticleInfo info = this->node_info(line);
    this->info = info;
    if (info.is_node) {
        this->ui->title_line_edit->setText(info.title);
        this->ui->title_line_edit->setEnabled(true);
        this->ui->content_text_edit->setPlainText(info.content);
        this->ui->content_text_edit->setEnabled(true);
    } else {
        this->ui->title_line_edit->setText("");
        this->ui->title_line_edit->setEnabled(false);
        this->ui->content_text_edit->setPlainText("");
        this->ui->content_text_edit->setEnabled(false);
    }
}

void MainWindow::delete_sub_node(int parent_id) {
    QSqlQuery *q = this->q_exec(
                "SELECT `id` FROM `node` WHERE `parent_node_id` = " +
                QVariant(parent_id).toString(), false);
    while (q->next()) {
        int id = q->value("id").toInt();
        QSqlQuery *temp = this->q_exec(
                    "DELETE FROM `node` WHERE `id` = " +
                    QVariant(id).toString(), false);
        delete temp;
        this->delete_sub_node(id);
    }
    delete q;
}

void MainWindow::on_delete_node_triggered() {
    ArticleInfo info = this->info;
    if (!info.is_node) {
        QMessageBox::warning(this, "System Warning", "Cannot delete " + info.title);
        return;
    }
    long ret = QMessageBox::warning(this, "System Warning", "确定要删除 '" + info.title + "' 吗？这将删除所有子节", QMessageBox::Yes, QMessageBox::No);
    if (ret != QMessageBox::Yes) {
        return;
    }

    QSqlQuery *q = this->q_exec(
                "DELETE FROM `node` "
                "WHERE `id` = " + QVariant(info.id).toString(), false);
    delete q;
    this->delete_sub_node(info.id);
    this->flush_tree();
}

void MainWindow::on_title_line_edit_textEdited(const QString &arg1) {
    ArticleInfo info = this->info;
    if (!info.is_node) {
        QMessageBox::warning(this, "System Warning", "Cannot edit " + info.title);
        return;
    }
    QStringList disable_string_list;
    disable_string_list << "'";
    for (QString str : disable_string_list) {
        if (arg1.contains(str)) {
            QMessageBox::warning(this, "System Warning", "Title has disabled substring.");
            return;
        }
    }
    QSqlQuery *q = this->q_exec(
                "UPDATE `node` SET `node_name` = '" + arg1 + "' "
                "WHERE `id` = " + QVariant(info.id).toString(), false);
    delete q;
}

void MainWindow::on_flush_triggered() {
    this->flush_tree();
}

void MainWindow::on_content_text_edit_textChanged() {
    ArticleInfo info = this->info;
    if (!info.is_node) {
        return;
    }
    QString content = this->ui->content_text_edit->toPlainText();
    QStringList disable_string_list;
    disable_string_list << "'";
    for (QString str : disable_string_list) {
        if (content.contains(str)) {
            QMessageBox::warning(this, "System Warning", "Content has disabled substring.");
            return;
        }
    }
    QSqlQuery *q = this->q_exec(
                "UPDATE `node` SET `content` = '" + content + "' "
                "WHERE `id` = " + QVariant(info.id).toString(), false);
    delete q;
}
